#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-

# chimera - observatory automation system
# Copyright (C) 2006-2007  P. Henrique Silva <henrique@astro.ufsc.br>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


from chimera.core.cli import ChimeraCLI, action, ParameterType

from chimera.util.coord import Coord
from chimera.util.position import Position

from chimera.core.callback import callback
from chimera.core.exceptions import ObjectNotFoundException 

import sys

#TODO: Abort, skip_init/init

class ChimeraTel (ChimeraCLI):
    
    def __init__ (self):
        ChimeraCLI.__init__(self, "chimera-tel", "Telescope controller", 0.1)
        
        self.localSlew = False
        
        self.addHelpGroup("TELESCOPE", "Telescope")
        self.addInstrument(name="telescope", cls="Telescope", required=True,
                           help="Telescope instrument to be used. If blank, try to guess from chimera.config",
                           helpGroup="TELESCOPE")
        
        self.addHelpGroup("COORDS", "Coordinates")
        self.addParameters(dict(name="ra", type="string", helpGroup="COORDS", help="Right Ascension."),
                           dict(name="dec", type="string", helpGroup="COORDS", help="Declination."),
                           dict(name="epoch", type="string", default="J2000", helpGroup="COORDS", help="Epoch"),
                           dict(name="az", type="string", helpGroup="COORDS", help="Local Azimuth."),
                           dict(name="alt", type="string", helpGroup="COORDS", help="Local Altitude."),
                           dict(name="objectName", long="object", type="string", helpGroup="COORDS", help="Object name"),)
        
        self.addHelpGroup("RATE", "Slew rate")
        self.addParameters(dict(name="rate", type=ParameterType.CHOICE, 
                                choices=["max", "MAX", "guide", "GUIDE", "center", "CENTER", "find", "FIND"],
                                default="MAX", helpGroup="RATE",
                                help="Slew rate to be used for --move-* commands. GUIDE, CENTER, FIND or MAX"))
        

        self.addHelpGroup("INIT", "Initialization")
        self.addHelpGroup("SLEW", "Slew")
        self.addHelpGroup("PARK", "Park")
        self.addHelpGroup("TRACKING", "Tracking")                
        self.addHelpGroup("HANDLE", "Virtual Handle",
                          "You can pass a int/float with number of arcseconds or use d:m:s notation. "
                          "Remember that this is an offset relative to the current position.")
        
        
    @action(help="Initialize the telescope (Lat/long/Date/Time)", helpGroup="INIT")
    def init(self, options):
        pass
    
    @action(help="Slew to given --ra --dec or --az --alt or --object", helpGroup="SLEW")
    def slew(self, options):
        
        telescope = self.telescope
        
        if options.objectName is not None:
            target = options.objectName
        else:
            target = self._validateCoords(options)
        
        @callback(self.localManager)
        def slewBegin(target):
            self.out(40*"=")
            if options.objectName:
                self.out("slewing to %s (%s) ... " % (options.objectName, target), end="")
            else:
                self.out("slewing to %s ... " % target, end="")

        @callback(self.localManager)
        def slewComplete(position):
            global telescope
            self.out("OK.")
            self.out(40*"=")
    
        @callback(self.localManager)
        def abortComplete(position):
            global telescope
            self.out("OK.")
            
        telescope.slewBegin     += slewBegin
        telescope.slewComplete  += slewComplete
        telescope.abortComplete += abortComplete    
    
        self.out(40*"=")
        self.out("current position ra/dec: %s" % telescope.getPositionRaDec())
        self.out("current position:alt/az: %s" % telescope.getPositionAltAz())
    
        if options.objectName:
            telescope.slewToObject(target)
        else:
            if self.localSlew:
                telescope.slewToAltAz(target)
            else:
                telescope.slewToRaDec(target)
    
        self.out("new position ra/dec: %s" % telescope.getPositionRaDec())
        self.out("new position:alt/az: %s" % telescope.getPositionAltAz())
        
        telescope.slewBegin     -= slewBegin
        telescope.slewComplete  -= slewComplete
        telescope.abortComplete -= abortComplete    

    @action(help="Sync on the given --ra --dec", helpGroup="SYNC")
    def sync(self, options):
        telescope = self.telescope
        
        target = self._validateCoords(options)
        
        self.out(40*"=")
        self.out("current position ra/dec: %s" % telescope.getPositionRaDec())
        self.out("current position:alt/az: %s" % telescope.getPositionAltAz())
        self.out(40*"=")

        self.out("syncing on %s ... " % target, end="")
        
        telescope.syncRaDec(target)

        self.out("OK")

        self.out(40*"=")
        self.out("new position ra/dec: %s" % telescope.getPositionRaDec())
        self.out("new position alt/az: %s" % telescope.getPositionAltAz())
        self.out(40*"=")        

    
    @action(help="Park the telescope", helpGroup="PARK", actionGroup="PARK")
    def park(self, options):
        self.out(40*"=")
        self.out("parking ... ", end="")
        self.telescope.park()
        self.out("OK")
        self.out(40*"=")        

    @action(help="Unpark the telescope", helpGroup="PARK", actionGroup="PARK")
    def unpark(self, options):
        self.out(40*"=")
        self.out("unparking ... ", end="")
        self.telescope.unpark()
        self.out("OK")
        self.out(40*"=")        
 
    @action(name="stop_tracking", long="stop-tracking", help="Stop telescope tracking",
            helpGroup="TRACKING", actionGroup="TRACKING")
    def stopTracking(self, options):
        self.out(40*"=")
        self.out("stopping telescope tracking... ", end="")
        self.telescope.stopTracking()
        self.out("OK")
        self.out(40*"=")        

    @action(name="start_tracking", long="start-tracking", help="Start telescope tracking",
            helpGroup="TRACKING", actionGroup="TRACKING")
    def startTracking(self, options):
        self.out(40*"=")
        self.out("starting telescope tracking... ", end="")
        self.telescope.getDriver().startTracking()
        self.out("OK")
        self.out(40*"=")        

    @action(help="Print telescope information and exit")
    def info(self, options):
        telescope = self.telescope

        self.out(40*"=")
        self.out("telescope:", telescope["driver"], "(%s)" % telescope.getDriver()["device"])
        self.out("current position ra/dec: %s" % telescope.getPositionRaDec())
        self.out("current position alt/az: %s" % telescope.getPositionAltAz())
        if telescope.isTracking():
            self.out("tracking: enabled.")
        else:
            self.out("tracking: disabled.")
            
        self.out(40*"=")

    def _move(self, direction, cmd, offset):
        
        offset = self._validateOffset(offset)
        
        self.out(40*"=")
        self.out("moving %s arcseconds (%s) %s at %s rate... " % (offset.AS,
                                                                  offset.strfcoord(),
                                                                  direction,
                                                                  self.options.rate), end="")
        try:
            cmd(offset.AS, self.options.rate)
            self.out("OK")
        except Exception, e:
            self.err("ERROR. (%s)" % e)
        
        self.out(40*"=")
    
    @action(name="move_east", short='E', long="move-east", type="string",
            help="Move telescope ARCSEC arcseconds to East.",
            metavar="ARCSEC", helpGroup="HANDLE")
    def moveEast(self, options):
        self._move("East", self.telescope.moveEast, options.move_east)
        
    @action(name="move_west", short='W', long="move-west", type="string",
            help="Move telescope ARCSEC arcseconds to West.",
            metavar="ARCSEC", helpGroup="HANDLE")
    def moveWest(self, options):
        self._move("West", self.telescope.moveWest, options.move_west)

    @action(name="move_north", short='N', long="move-north", type="string",
            help="Move telescope ARCSEC arcseconds to North.",
            metavar="ARCSEC", helpGroup="HANDLE")
    def moveNorth(self, options):
        self._move("North", self.telescope.moveNorth, options.move_north)
    
    @action(name="move_south", short='S', long="move-south", type="string",
            help="Move telescope ARCSEC arcseconds to South.",
            metavar="ARCSEC", helpGroup="HANDLE")
    def moveSouth(self, options):
        self._move("South", self.telescope.moveSouth, options.move_south)        

    def _validateCoords (self, options):
        
        target = None
        
        if (options.ra != None or options.dec != None) and \
            (options.az != None or options.alt != None):
            self.exit("RA/DEC and AZ/ALT given at the same time, I don't know what to do.")

        if (options.ra != None) and (options.dec != None):
            try:
                target = Position.fromRaDec(options.ra, options.dec, options.epoch)
            except Exception, e:
                self.exit(str(e))

        elif (options.az != None) and (options.alt != None):
            try:
                target = Position.fromAltAz(options.alt, options.az)
                self.localSlew = True
            except Exception, e:
                self.exit(str(e))

        else:
            self.exit("Invalid coordinates, try --ra --dec or --alt --az or --object.")
        
        return target
        
    def __abort__ (self):
        print >> sys.stdout, "aborting... ",
        sys.stdout.flush()

        tel = copy.copy(telescope)
        if tel.isSlewing():
            tel.abortSlew()

    def _validateOffset(self, value):
        try:
            offset = Coord.fromAS(int(value))
        except ValueError:
            offset = Coord.fromDMS(value)
        
        return offset

def main():
    cli = ChimeraTel()
    return cli.run(sys.argv)
    
if __name__ == '__main__':
    main()

